import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class WordCountCore {
    public static String[] fileProcess(String filePath) throws IOException {
        StringBuilder buffer = new StringBuilder();
        InputStream file = new FileInputStream(filePath);
        int size = file.available();

        for (int i = 0; i<size;i++){
            char byt = (char)file.read();
            //Check illegal character
            if (byt > 127){
                System.out.println("[-]Input pure ASCII file only!");
                System.exit(1);
            }
            //Convenient for split
            if(byt == '\n'){
                byt = ' ';
            }
            //Filter exclude number and letter
            else if(byt == '\r'      ||
                    byt>='!' && byt<='/' ||
                    byt>=':' && byt<='@' ||
                    byt>='[' && byt<='`' ||
                    byt>='{'
            ){
                continue;
            }
            buffer.append(byt);
        }

        String text = buffer.toString().toLowerCase();
        String[] division = text.split(" ");
        return division;
    }

    public static LinkedHashMap<String,Integer> wordCounter(String[] division){
        TreeMap<String,Integer> counter = new TreeMap<>();
        LinkedHashMap<String,Integer> result = new LinkedHashMap<>();

        for (String s : division) {
            if(stringChecker(s)){
                continue;
            }
            if (counter.containsKey(s)) {
                Integer count = counter.get(s);
                counter.put(s, count + 1);
            } else {
                counter.put(s, 1);
            }
        }

        for (int i = 0; i<10; i++){
            String maxWord = "";
            Integer maxNum = 0;
            for (String word : counter.keySet()){
                Integer currentNum = counter.get(word);
                if(currentNum > maxNum){
                    maxNum = currentNum;
                    maxWord = word;
                }
            }
            counter.remove(maxWord);
            if(!maxWord.equals("") && maxNum != 0){
                result.put(maxWord, maxNum);
            }
        }
        return result;
    }

    public static boolean stringChecker(String word){
        char[] letter = word.toCharArray();
        if (letter.length<4){
            return true;
        }
        for (char c: letter){
            if(c<97 || c>122){
                return true;
            }
        }
        return false;
    }

    public static void resultOutput(String key, String value, OutputStream outFile) throws IOException {
        outFile.write(key.getBytes());
        outFile.write(':');
        outFile.write(' ');
        outFile.write(value.getBytes());
        outFile.write('\n');
    }

    public static void countWordFrequency(String inputPath , String outputPath) throws IOException {
        String[] dividedWord = fileProcess(inputPath);
        OutputStream outFile = new FileOutputStream(outputPath);
        LinkedHashMap<String,Integer> resultMap =  wordCounter(dividedWord);
        resultOutput("characters",String.valueOf(countChar(inputPath)), outFile);
        resultOutput("words",String.valueOf(countWord(inputPath)), outFile);
        resultOutput("lines",String.valueOf(countLine(inputPath)), outFile);
        //Write Frequency number
        for (String word : resultMap.keySet()){
            String num = resultMap.get(word).toString();
            resultOutput(word, num, outFile);
        }
        outFile.close();
        System.out.println("[+]Counter have run successfully!");
    }

    public static int countWord(String inputPath)throws IOException{
        int count = 0;
        String[] dividedWord = fileProcess(inputPath);
        for (String s : dividedWord) {
            if(!stringChecker(s)){
                count++;
            }
        }
        return count;
    }

    public static int countChar(String inputPath)throws IOException{
        InputStream file = new FileInputStream(inputPath);
        return file.available();
    }

    public static long  countLine(String inputPath)throws IOException{
        return Files.lines(Paths.get(inputPath)).count();
    }

}
